Add keychain and derivation index to unspent list#75
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #75 +/- ##
=======================================
Coverage 95.74% 95.75%
=======================================
Files 23 23
Lines 12835 12857 +22
=======================================
+ Hits 12289 12311 +22
Misses 546 546
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
The code has several issues, both on implementation, like usage of But before discussing these issues I would like to understand if this method is actually needed. From what I see, everything can already be done with what rgb-lib provides, just call |
|
@zoedberg
The caller would need to duplicate the BDK wallet from the same descriptors, correct BIP-32 tree, witness version, keychain index, network-aware seed derivation. I'd rather not push that duplication onto callers. More importantly, signing an outpoint isn't a free operation outside the library. BDK's derivation_of_spk only knows about indices it has indexed, so the duplicated wallet has to be synced against a chain source first (or the caller has to manually reveal addresses up to the right index) before the scriptPubKey -> (keychain, index) lookup works. Otherwise it returns None and the only way to recover the index is brute-forcing the child range until a derivation matches. rgb-lib already has a synced wallet with this state populated, so doing it inside is essentially free. Doing it outside means a full sync just to sign a message. |
|
BDK has a way to sync a single SPK, I think you could use that to avoid syncing a lot of unnecessary SPKs. With that you can do this operation outside rgb-lib without a noticeable overhead. |
|
Single-SPK sync helps with chain sync, but the bottleneck is the lookup itself. Given an outpoint from list_unspents, the caller doesn't know the (keychain, derivation_index) pair. To find it they have to derive children 0..N on the colored keychain, apply the tap-tweak (or not, depending on witness version), and compare each derived SPK against the outpoint's scriptPubKey until one matches. Single-SPK sync doesn't shortcut that, it's a derivation problem, not a network one. The state that does shortcut it is BDK's reverse SPK -> (keychain, index) map, which only contains entries for already-revealed indices. rgb-lib's wallet has this populated as part of normal operation; an external caller starting from a fresh duplicated wallet has to either rebuild it (= reveal/derive up to N) or brute-force on every signing. Plus, to derive at all the caller needs the descriptors and the witness version, wallet-internal conventions they'd have to read out of SinglesigKeys and apply correctly. And there's the README:
I realise the strict reading of that is about operating on UTXOs from a second wallet, not read-only key derivation, so signing without broadcasting is technically safe. But it's still using the mnemonic somewhere other than rgb-lib, and I'd really rather not push callers toward that pattern, even for "harmless" signing operations, it's the kind of thing that drifts over time. |
|
What if we add the keychain and derivation index information to the unspent list? |
|
@zoedberg To better understand the preferred API boundary: what is the main reason you would not want rgb-lib itself to provide a controlled signing/proof method for wallet-owned UTXOs? Is the concern mostly about keeping rgb-lib’s API surface and maintenance burden minimal, avoiding responsibility for generic Bitcoin signing operations, or something else? From my perspective, proving ownership of an RGB asset feels like something that naturally belongs in an RGB library, especially if rgb-lib already owns the wallet state and key-derivation context needed to do it safely and consistently. I may be biased here since this is my use case, but that is why I initially expected this functionality to fit within rgb-lib. That said, I am open to changing the API/interface if there is a better shape for this functionality or if a more general signing/proof abstraction would fit the library better. |
|
The main concern is keeping the rgb-lib's public APIs clear and maintainable. Adding a new API to provide functionality for a specific use case is outside the scope of the project, which aims to provide an abstraction over RGB primitives that is as simple as possible while retaining the needed flexibility. Adding info to an existing API which enables more use cases fits with the goals and doesn't impact maintenance much. As for proving ownership of UTXOs (not RGB assets specifically), it doesn't look like something that adds value to rgb-lib, as for all current intents and purposes, ownership is already proven when spending and there are no cases where proving control over a UTXO seems useful on its own. |
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
c1cab64 to
90fa78b
Compare
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
90fa78b to
931196f
Compare
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
931196f to
38101ea
Compare
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
38101ea to
9b511e3
Compare
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
9b511e3 to
c24bf1d
Compare
Add keychain and derivation_index fields to Utxo, as agreed in the discussion on PR RGB-Tools#75, so callers can derive the corresponding key externally (e.g. to prove asset ownership).
c24bf1d to
296cad8
Compare
zoedberg
left a comment
There was a problem hiding this comment.
Thanks! The implementation now matches what we discussed in the thread, I updated the PR title accordingly. I requested some changes but overall looks good to me
| .map(|(i, u)| (BdkOutPoint::from(u.utxo.outpoint.clone()), i)) | ||
| .collect(); | ||
| let mut vanilla_unspents: Vec<Unspent> = vec![]; | ||
| for output in self.bdk_wallet().list_output() { |
There was a problem hiding this comment.
list_output() iterates all spent + unspent outputs; that feels heavy for every list_unspents call. Could we enrich colored UTXOs with get_tx + derivation_of_spk and keep internal_unspents() for vanilla? That should also handle almost-spent colored UTXOs without a full graph walk.
| let derived_spk = wallet | ||
| .bdk_wallet() | ||
| .public_descriptor(keychain) | ||
| .at_derivation_index(unspent.utxo.derivation_index.unwrap()) |
There was a problem hiding this comment.
Tests currently only cover Taproot wallets. Would you mind adding a SegWitV0 case so we're confident at_derivation_index / descriptor handling works for both witness versions?
This method adds a way to prove the ownership of an asset. We need it to allow access to a service related to an asset.
If there is anything to improve, please let me know. The message is there to prevent signature-reuse.